import socket
from urllib.parse import urlparse

from hashlib import md5


config_dict = {
    'cseq': 2,
    'user_agent': 'LibVLC/3.0.2 (LIVE555 Streaming Media v2016.11.28)',
    'timeout': 3,
    'recvbite': 4096,
    'res_status': '200 OK',
    'rtsp_status': 'flase'
}
clientports=[60784, 60785]

def options_get(url):
    '''
    options请求检测
    url: rtsp流地址
    return: options请求相应
    '''
    url = urlparse(url)
    host = url.netloc
    hostname = url.hostname
    path = url.path
    port = url.port
    str_options = 'OPTIONS rtsp://' + str(host) + \
                         path + ' RTSP/1.0\r\n'
    str_options += 'CSeq: ' + str(config_dict['cseq']) + '\r\n'
    str_options += 'User-Agent: ' + config_dict['user_agent'] + '\r\n'
    str_options += '\r\n'
    print("str_options: ", str_options)

    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.settimeout(config_dict['timeout'])
    client.connect((hostname, port))
    client.send(str_options.encode())
    d = client.recv(config_dict['recvbite'])
    return d
def describe_get(url):
    '''
    describe请求检测
    url: rtsp流地址
    return: describe请求相应
    '''
    url = urlparse(url)
    host = url.netloc
    hostname = url.hostname
    path = url.path
    port = url.port
    str_describe = 'DESCRIBE rtsp://' + str(host) + \
                         path + ' RTSP/1.0\r\n'
    str_describe += 'CSeq: ' + str(config_dict['cseq'] + 1) + '\r\n'
    str_describe += 'User-Agent: ' + config_dict['user_agent'] + '\r\n'
    str_describe += '\r\n'
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.settimeout(config_dict['timeout'])
    client.connect((hostname, port))
    client.send(str_describe.encode())
    d = client.recv(config_dict['recvbite'])
    return d
def setup_get(url):
    '''
    setup请求检测
    url: rtsp流地址
    return: setup请求相应
    '''
    url = urlparse(url)
    host = url.netloc
    hostname = url.hostname
    path = url.path
    port = url.port
    str_setup = 'SETUP rtsp://' + str(host) + path + '/' + 'streamid=0' + ' RTSP/1.0\r\n'
    str_setup += 'CSeq: ' + str(config_dict['cseq'] + 2) + '\r\n'
    str_setup += 'User-Agent: ' + config_dict['user_agent'] + '\r\n'
    # config_dict['user_agent']
    str_setup += 'Transport: RTP/AVP;unicast;client_port=61740-61741\r\n\r\n'
    str_setup += '\r\n'
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.settimeout(config_dict['timeout'])
    client.connect((hostname, port))
    client.send(str_setup.encode())
    d = client.recv(config_dict['recvbite'])
    return d
def teardown_get(url):
    '''
    teardown请求检测
    url: rtsp流地址
    return: teardown请求相应
    '''
    url = urlparse(url)
    host = url.netloc
    hostname = url.hostname
    path = url.path
    port = url.port
    str_teardown = 'TEARDOWN rtsp://' + str(host) + path + ' RTSP/1.0\r\n'
    str_teardown += 'CSeq: ' + str(config_dict['cseq'] + 4) + '\r\n'
    str_teardown += 'User-Agent: ' + config_dict['user_agent'] + '\r\n'
    str_teardown += '\r\n'
    print(str_teardown)
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.settimeout(config_dict['timeout'])
    client.connect((hostname, port))
    client.send(str_teardown.encode())
    d = client.recv(config_dict['recvbite'])
    return d
def send_main(url):
    try:
        str_options = str(options_get(url))
        print(str_options)
        if config_dict['res_status'] in str_options:
            str_des = str(describe_get(url))
            print(str_des)
            if config_dict['res_status'] in str_des:
                str_setup = str(setup_get(url))
                str_teardown = str(teardown_get(url))
                print(str_setup)
                print(str_teardown)
                if config_dict['res_status'] in str_teardown:
                    config_dict['rtsp_status'] = 'true'
                    return True
    except Exception:
        return False
    else:
        return False


def rtsp_authorize(auth_data, user="admin", password="abcd1234", method="GET", uri="/ISAPI/Security/userCheck"):
    nonce = auth_data["nonce"]
    realm = auth_data.get("realm", None)
    if realm is None:
        realm = auth_data.get("Digest realm", None)
    if realm is None:
        print("realm/Digest realm error...")
        return None

    A1 = "{}:{}:{}".format(user, realm, password)
    A2 = "{}:{}".format(method, uri)
    print("A1: ", A1)
    print("A2: ", A2)

    A1_md5 = md5(A1.encode("utf8"))
    A2_md5 = md5(A2.encode("utf8"))
    # print("A1_md5: ", A1_md5.hexdigest())
    # print("A2_md5: ", A2_md5.hexdigest())

    response = "{}:{}:{}".format(
                                A1_md5.hexdigest(), 
                                nonce,
                                A2_md5.hexdigest())
    response_md5 = md5(response.encode("utf8")).hexdigest()
    # print("\nresponse: ", response)
    # print("response_md5: ", response_md5)
    
    authorization = "{}=\"{}\", realm=\"{}\", nonce=\"{}\", uri=\"{}\", response=\"{}\"".format(
        "Authorization:Digest username",
        user,
        realm,
        nonce,
        uri,
        response_md5
    )
    return authorization


def rtsp_describle(ip, uri, auth=None, port=554, CSeq=0, timeout=3, recvbite=4096):
    '''
    describe请求检测
    url: rtsp流地址
    return: describe请求相应
    '''
    str_describe = 'DESCRIBE rtsp://{}:{}{} RTSP/1.0\r\n'.format(ip, port, uri)
    str_describe += 'CSeq: {}\r\n'.format(CSeq)
    if auth:
        str_describe += auth + "\r\n"
    str_describe += 'User-Agent: LibVLC/2.2.8 (LIVE555 Streaming Media v2016.02.22)' + '\r\n'
    str_describe += "Accept: application/sdp\r\n"
    str_describe += '\r\n'
    print(str_describe)

    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.settimeout(timeout)
    client.connect((ip, port))
    client.send(str_describe.encode())
    d = client.recv(recvbite)
    return d.decode("utf8")

def parse_data(lines): 
    # print(lines)
    lines = lines.split("\r\n")
    data = {}
    head = lines[0]
    heads = head.split(" ")
    if len(heads) != 3: return None

    data["head"] = heads
    for l in lines[1:]:
        if l== "": continue
        s = l.split(": ")
        data[s[0]] = s[1]

    auth = data.get("WWW-Authenticate", None)
    if auth is None: return None
    auths = auth.split(", ")
    auth_dict = {}
    for a in auths:
        kv = a.split("=")
        auth_dict[kv[0]] = kv[1].replace("\"", "")
    data["WWW-Authenticate"] = auth_dict
    print(data)
    return data

def main(ip, uri, port=554):
    CSeq = 0
    msg = rtsp_describle(ip, uri, CSeq=CSeq)
    data = parse_data(msg)
    if data is None: return data
    if data["head"][1] == "401":
        url = 'rtsp://{}:{}{}'.format(ip, port, uri)
        auth = rtsp_authorize(data["WWW-Authenticate"], method="DESCRIBE", uri=uri)
        if auth is None: return auth
        CSeq+=1
        msg = rtsp_describle(ip, uri, auth=auth, CSeq=CSeq)
    data = parse_data(msg)

if __name__ == "__main__":
    print(send_main('rtsp://192.168.10.214:554/live/av0'))

    # ip = "192.168.104.119"
    # uri = "/ISAPI/Streaming/thermal/channels/1/streamType/pixel-to-pixel_thermometry_data"
    # main(ip, uri)
